<?php
/**
 * Joomla! 1.5 component JoomGPSTracks
 *
 * @version $Id: gpsClass.php 2009-06-24 15:40:19 svn $
 * @author Michael Pfister
 * @package joomGPStracks
 * @subpackage frontend
 * @license GNU/GPL
 * @filesource
 *
 */
 
// no direct access
defined('_JEXEC') or die('Restricted access');
/**
 * Mainclass to write the google map
 */
class gpsClass  {

    var $gpsFile        = NULL;


    /**
     *
     * @return object
     */
    public function loadFile()  {

        if (file_exists($this->gpsFile)) {
            $xml = simplexml_load_file($this->gpsFile);

            return $xml;
        } else {
            return false;
        }
    }

    /**
     *
     * @return array
     */
    public function getStartCoordinates()  {
        jimport('joomla.filesystem.file');

        if(JFile::getExt($this->gpsFile) == 'kml'):
            $start = $this->getStartKML($this->gpsFile);
        endif;

        if(JFile::getExt($this->gpsFile) == 'gpx'):
            $start = $this->getStartGPX();
        endif;

        if(JFile::getExt($this->gpsFile) == 'tcx'):
            $start = $this->getStartTCX();
        endif;

        return $start;

    }

    /**
     *
     * @return array
     */
    private function getStartKML($file)  {

        $coords = $this->getCoords($file);

        $start = $coords[0];

        return $start;
        
    }

    /**
     *
     * @return array
     */
    private function getStartGPX()  {
        $xml = $this->loadFile();

        $startpoint = $xml->trk->trkseg->trkpt[0]->attributes();
        $start = array((string)$startpoint['lon'],(string)$startpoint['lat']);

        return $start;
    }

    private function getStartTCX()  {
        $xml = $this->loadFile();

        if(isset($xml->Activities->Activity->Lap->Track)) {
            $startpoint = $xml->Activities->Activity->Lap->Track[0]->Trackpoint;
        } elseif (isset($xml->Courses->Course->Track)) {
            $startpoint = $xml->Courses->Course->Track[0]->Trackpoint;
        }

        $lat = $startpoint->Position->LatitudeDegrees;
        $lon = $startpoint->Position->LongitudeDegrees;

        $start = array((string)$lon,(string)$lat);

        return $start;
    }

    /**
     *
     * @global object $mainframe
     * @return array
     */
    function getMapNates()  {
        global $mainframe;

        $db =& JFactory::getDBO();

        $query = "SELECT start_n FROM #__gps_tracks"
                . "\n ORDER BY start_n ASC"
                . "\n LIMIT 1";
                
        $db->setQuery($query);
        $north = $db->loadResult();

        $query = "SELECT start_e FROM #__gps_tracks"
                . "\n ORDER BY start_e DESC"
                . "\n LIMIT 1";

        $db->setQuery($query);
        $east = $db->loadResult();

        $cnates = array();
        $cnates[] = $north;
        $cnates[] = $east;

        return $cnates;
        
    }

    /**
     *
     * @global object $mainframe
     * @return array
     */
    function getTracks()  {
        global $mainframe;

        $db = JFactory::getDBO();

        $query = "SELECT a.*, b.title AS cat FROM #__gps_tracks AS a"
                . "\n LEFT JOIN #__gps_cats AS b"
                . "\n ON a.catid=b.id"
                ;

        $db->setQuery($query);
        $rows = $db->loadObjectList();

        return $rows;
    }

    /**
     *
     * this writes the Javascript code for the overview map
     * the map is shown in a div container with <strong>id="map"</strong>
     * <code>
     * $gps = new gpsClass();
     * $track = loadObject($query);
     * $gps->writeMap($track);
     * </code>
     * 
     * @return string Javascriptcode
     */
    public function writeMap()  {

        $cnates = $this->getMapNates();
        $rows = $this->getTracks();

        $map = '';
        $map .= '<script type="text/javascript">';
        $map .= "//<![CDATA[\n";
        $map .= 'google.load("maps", "2");';
        $map .= 'google.load("search", "1");';
        $map .= 'var map;';
        $map .= $this->markerFunction();
        $map .= 'function initialize() {';
        $map .= $this->parseMap();
        $map .= 'var bounds = new GLatLngBounds();';
        
        $map .= $this->parseMapControl();
        $map .= 'map.addControl(new google.maps.LocalSearch(), new GControlPosition(G_ANCHOR_BOTTOM_LEFT, new GSize(10,20)));';
        $map .= $this->parseMarkers();
        $map .= 'map.setCenter(bounds.getCenter(), map.getBoundsZoomLevel(bounds));';
        $map .= $this->parseScriptFooter();

        return $map;
    }

    /**
     * this function shows the map for a single track
     * the map is shown in a div container with id="map"
     * $track is the database object of the track
     * <code>
     * $gps = new gpsClass();
     * $track = loadObject($query);
     * $gps->writeTrack($track);
     * </code>
     *
     * @global object $mainframe
     * @param object $track
     * @return string Javascriptcode
     */
    public function writeTrack($track)  {
        global $mainframe;
        jimport('joomla.filesystem.file');

        $file = "components/com_joomgpstracks/uploads/".$track->file;
        $map = "";

            $map .= $this->parseScriptHead();
            $map .= $this->parseMap();
            $map .= 'var bounds = new GLatLngBounds();';
            $map .= $this->parseMapControl();
            $map .= $this->parseStartPoint($track);
            $map .= $this->parseXMLlines("./".$file);
            $map .= "map.setCenter(bounds.getCenter(), map.getBoundsZoomLevel(bounds));\n";
            $map .= $this->parseScriptFooter();

        return $map;
    }

    /**
     *
     * @return string
     */
    private function parseScriptHead()  {

        $map = "";
        $map .= "\n<script type='text/javascript'>\n";
        $map .= "//<![CDATA[\n";
        $map .= "google.load('maps', '2');\n";
        $map .= "var map\n";
        $map .= "function initialize() {\n";

        return $map;
    }

    /**
     *
     * @return string
     */
    private function parseScriptFooter()  {

        $map = "";
        $map .= "}\n";
        $map .= "google.setOnLoadCallback(initialize);\n";
        $map .= "//]]>\n";
        $map .= "</script>\n";

        return $map;
    }

    /**
     *
     * @return string
     */
    private function parseMapControl()  {

        $control = "";
        $control .= "var mapControl = new GMapTypeControl();\n";
        $control .= "map.addControl(mapControl);\n";
        $control .= "map.addControl(new GLargeMapControl());\n";
        $control .= "map.addControl(new GOverviewMapControl());\n";

        return $control;
    }

    /**
     *
     * @return string
     */
    private function markerFunction()  {
        $map = "";
        $map .= "function createMarker(point,html) {\n";
        $map .= "var marker = new GMarker(point);\n";
        $map .= "GEvent.addListener(marker, 'click', function() {\n";
        $map .= "marker.openInfoWindowHtml(html);\n";
        $map .= "});\n";
        $map .= "return marker;\n";
        $map .= "}\n";

        return $map;
    }

    /**
     *
     * @return string
     */
    private function parseMarkers()  {
        $rows = $this->getTracks();
        $map = "";

        foreach($rows as $row)  {
            $link = JROUTE::_("index.php?option=com_joomgpstracks&view=tracks&layout=track&id=".$row->id);
            $map .= 'var point = new GLatLng('.$row->start_n.', '.$row->start_e.');';
            $map .= 'bounds.extend(point);';
            $map .= 'var marker = createMarker(point,"<a href=\"'.$link.'\"><b>'.$row->title.'</b></a><br />'.JText::_('GPS_CAT').': '.$row->cat.'");';
            $map .= 'map.addOverlay(marker);';
        }

        return $map;
    }
    
    /**
     *
     * @return string
     */
    private function parseMap()  {
        $cfg =& JoomgpstracksHelper::getConfig();
        switch($cfg->map_type)  {
            case "0":
                $type = "G_NORMAL_MAP";
                break;

            case "1":
                $type = "G_SATELLITE_MAP";
                break;

            case "2":
                $type = "G_HYBRID_MAP";
                break;

            case "3":
                $type = "G_PHYSICAL_MAP";
                break;

            default:
                $type = "G_NORMAL_MAP";
                break;
        }
        
        $string = "";
        $string .= "map = new GMap2(document.getElementById('map'));\n";
        $string .= "map.addMapType(G_PHYSICAL_MAP);\n";
        $string .= "map.addMapType(G_SATELLITE_3D_MAP);\n";
        $string .= "map.setMapType(".$type.");\n";

        return $string;
    }

    /**
     *
     * @param string $url
     * @return string
     */
    private function parseKml($url)
    {
        if ($url)
        {
            $string = "var geoXml = new GGeoXml(\"$url\");\n
		map.addOverlay(geoXml);\n";
            return $string;
        }
        else
        {
            return true;
        }
    }

    /**
     *
     * @param object $track
     * @return string
     */
    private function parseStartPoint($track)  {
        $map = "";
            $map .= "var blueIcon = new GIcon(G_DEFAULT_ICON);\n";
            $map .= "blueIcon.image = '".JURI::base()."components/com_joomgpstracks/assets/images/gruen.png';\n";
            $map .= "markerOptions = { icon:blueIcon };\n";
            $map .= "var point = new GLatLng(".$track->start_n.",".$track->start_e.")\n";
            $map .= "map.addOverlay(new GMarker(point, markerOptions));\n";

            return $map;
    }

    /**
     * counts the distance of a track
     * $koords look like this: $array($point1(array(lat,lon)),$point2(array(lat,lon)))...
     * 
     * @param array $koord
     * @return int kilometers
     */
    public function getDistance($koord)
    {
        if (!is_array($koord))
        {
            return false;
        } else {

        $ent = 0;
        $welt = 6378.137; // Erdradius, ca. Angabe
        $last = array_pop($koord);
        foreach($koord as $key => $fetch)
        {
            if (isset($koord[$key + 1]))
            {
                $erste_breite = $koord[$key][1]; // lat
                $erste_laenge = $koord[$key][0]; // lon
                $erste_breite_rad = deg2rad($erste_breite);
                $erste_laenge_rad = deg2rad($erste_laenge);

                $zweite_breite = $koord[$key + 1][1]; // lat
                $zweite_laenge = $koord[$key + 1][0]; // lon
                $zweite_breite_rad = deg2rad($zweite_breite);
                $zweite_laenge_rad = deg2rad($zweite_laenge);

                $dis = acos(
                    (sin($erste_breite_rad) * sin($zweite_breite_rad)) +
                    (cos($erste_breite_rad) * cos($zweite_breite_rad) *
                        cos($zweite_laenge_rad - $erste_laenge_rad))) * $welt;

                if(is_nan($dis)) {
                    $ent = $ent;
                } else {
                    $ent = $ent + $dis;
                }
            }
        }
        $entfernung = $ent;
        return round($entfernung, 2);
        }
    }

    /**
     * checks if the given file is a GPX or KML file and call the function for it
     * 
     * @param string $file
     * @return array
     */
    public function getCoords($file)  {
        jimport('joomla.filesystem.file');

        $ext = JFile::getExt($file);

        if($ext == 'kml')  {
            $coords =$this->getCoordsKML($file);
            return $coords;
        } else if($ext == 'gpx')  {
            $coords = $this->getCoordsGPX($file);
            return $coords;
        } else if ($ext == 'tcx')  {
            $coords = $this->getCoordsTCX($file);
            return $coords;
        } else {
            return JText::_('GPS_FILE_ERROR');
        }

    }

    /**
     *
     * @param string $file
     * @return array
     */
    private function getCoordsKML($file)  {

        if (file_exists($file)) {
            $xml = simplexml_load_file($file);
            $namespaces = $xml->getNamespaces();

            $newns = array();
            foreach($namespaces as $key => $ns)  {
                    array_push($newns, $ns);
            }

            $doc = new DOMDocument;
            $doc->load($file);

            $xpath = new DOMXPath($doc);
            $xpath->registerNamespace('kml', $newns[0]);

            $query = $xpath->query('//kml:LineString/kml:coordinates/text()');

            $coords = $query->item(0)->nodeValue;
            // catch different types coordinates are written in kml files
            $coords = str_replace("\n", '/',$coords);
            $coords = str_replace(" ", '/',$coords);
            
            $coords = explode('/', $coords);
            $newco = array();
            for($i=0, $n=count($coords); $i<$n; $i++)  {
                if($coords[$i] != NULL)  {
                    $file = explode(',', $coords[$i]);
                    $a = array($file[0],$file[1],$file[2],0,0);
                    array_push($newco, $a);
                }
            }
            $start = array_pop($newco);
            
            return $newco;

            } else {
                return false;
            }
        
    }

    /**
     *
     * @param string $file
     * @return array
     */
    private function getCoordsGPX($file)  {

        if (file_exists($file)) {
            $xml = simplexml_load_file($file);
            $startpoint = $xml->trk->trkseg->trkpt;
            $start = array();
            for($i=0, $n=count($startpoint); $i<$n; $i++)  {
                    if(isset($startpoint[$i]->attributes()->lat) && isset($startpoint[$i]->attributes()->lon)) {
                    $lat = $startpoint[$i]->attributes()->lat;
                    $lon = $startpoint[$i]->attributes()->lon;
                    if(isset($startpoint[$i]->ele)) {
                        $ele = $startpoint[$i]->ele;
                    } else {
                        $ele = "0";
                    }
                    if(isset($startpoint[$i]->time)) {
                        $time = $startpoint[$i]->time;
                    } else {
                        $time = "0";
                    }

                    $bak = array((string)$lon, (string)$lat,(string)$ele,(string)$time,0);
                    array_push($start, $bak);
                }
            }
            

            return $start;
        } else {
            return false;
        }
        
    }

    /**
     *
     * @param string $file
     * @return array
     */
    private function getCoordsTCX($file)  {
        if (file_exists($file)) {
            $xml = simplexml_load_file($file);

            if(isset($xml->Activities->Activity->Lap->Track)) {
                $startpoint = $xml->Activities->Activity->Lap->Track[0];
            } elseif (isset($xml->Courses->Course->Track)) {
                $startpoint = $xml->Courses->Course->Track[0];
            }

            $coords = array();

            foreach($startpoint[0] as $start) {
                if(isset($start->Position->LatitudeDegrees) && isset($start->Position->LongitudeDegrees)) {
                    $lat = $start->Position->LatitudeDegrees;
                    $lon = $start->Position->LongitudeDegrees;
                    $ele = $start->AltitudeMeters;
                    $time = $start->Time;
                    if(isset($start->HeartRateBpm->Value)) {
                        $heart = $start->HeartRateBpm->Value;
                    } else {
                        $heart = "0";
                    }

                    $bak = array((string)$lon,(string)$lat,(string)$ele,(string)$time,(string)$heart);
                    array_push($coords,$bak);
                }
            }
            return $coords;
        } else {
            return false;
        }
    }

    /**
     *
     * @param string $file
     * @return array
     */
    private function parseXMLlines($file)  {

        $coords = $this->getCoords($file);

        $string = "";
        $string .= "var polyline = new GPolyline([\n";
        foreach($coords as $key => $fetch)  {
            $string .= "new GLatLng(".$coords[$key][1].",".$coords[$key][0]."),\n";
        }       
        $string .= "], '#ff0000', 4);\n";
        $string .= "map.addOverlay(polyline);\n";
        foreach($coords as $key => $fetch)  {
            $string .= "bounds.extend(new GLatLng(".$coords[$key][1].",".$coords[$key][0]."));";
        }

        return $string;
        print_r($coords);
    }


    /**
     *
     * @param array $coords
     * @return array
     */
    public function getElevation($coords)  {
        
        $asc = 0;
        $desc = 0;
        for($i=0, $n=count($coords); $i<$n; $i++)  {
                if($coords[$i][2] < $coords[$i+1][2]):
                        $asc = $asc + ($coords[$i+1][2] - $coords[$i][2]);
                endif;
        }

        for($i=0, $n=count($coords); $i<$n-1; $i++)  {
                if($coords[$i][2] > $coords[$i+1][2]):
                        $desc = $desc + ($coords[$i][2] - $coords[$i+1][2]);
                endif;
        }

        $ele = array(round($asc,0),round($desc,0));

        return $ele;

    }

    /**
     *
     * @param array $coords
     * @return string
     */
    public function createChartsData($coords)  {
        $cht = "";

        $n = count($coords);
        if($n > 600)  {
            $c = $n / 600;
            $c = round($c,0);
        } else {
            $c = 1;
        }
        $ele = array();
        for($i=0; $i<$n; $i = $i+$c)  {
            $coord = $coords[$i];
            $cht .= round($coord[2],0).",";
            array_push($ele,round($coord[2],0));
        }
        $chtn = substr($cht, 0, -1);

        return $chtn;
    }

        public function createBeatsData($coords)  {
        $cht = "";

        $n = count($coords);
        if($n > 600)  {
            $c = $n / 600;
            $c = round($c,0);
        } else {
            $c = 1;
        }
        $beats = array();
        for($i=0; $i<$n; $i = $i+$c)  {
            $coord = $coords[$i];
            $cht .= $coord[4].",";
            array_push($beats,$coord[4]);
        }
        $chtn = substr($cht, 0, -1);

        return $chtn;
    }

    /**
     *
     * @param string $distance
     * @return string
     */
    public function distance2chart($distance)  {
        $end = round($distance,1);
        $first = round($distance/4,1);
        $mid = round($distance/2,1);
        $third = round($first * 3,1);
        $dis = "0|".$first."|".$mid."|".$third."|".$end;
        
        return $dis;
    }

}
?>
